﻿using System.Collections.ObjectModel;
using System.Linq;
using Dino;
using NUnit.Framework;


namespace Tests.Dino.EntityFramework
{
    public partial class ObjectContextTests
    {
        [TestFixture]
        public class WhenQuerying
        {
            private TestContext _context;

            [SetUp]
            public void SetUp()
            {
                _context = new TestContext();
                _context.Database.CreateIfNotExists();
            }

            [TearDown]
            public void TearDown()
            {
                _context.Database.Delete();
            }

            [Test]
            public void Empty_Entity_Set_Returned()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var results = unitOfWork1.GetContext<TestContext>().Query<Entity>();

                Assert.AreEqual(0, results.Count());
            }

            [Test]
            public void Adding_An_Entity_Adds_To_Underlying_Context_On_Commit()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();
                var unitOfWork2 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => new TestContext())).Initialize();

                var foo = 5;
                var entityToAdd = new Entity {Foo = foo};

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var results = unitOfWork2.GetContext<TestContext>().Query<Entity>();

                Assert.AreEqual(1, results.Count());
                var actual = results.First();
                Assert.AreNotSame(entityToAdd, actual);
                Assert.AreEqual(foo, actual.Foo);
            }

            [Test]
            public void Adding_An_Entity_Without_Commit_Does_Not_Add_To_Context()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();
                var unitOfWork2 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => new TestContext())).Initialize();

                var foo = 5;
                var entityToAdd = new Entity { Foo = foo };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);

                var results = unitOfWork2.GetContext<TestContext>().Query<Entity>();

                Assert.AreEqual(0, results.Count());
            }

            [Test]
            public void Getting_An_Entity_After_Add_Is_Committed_Returns_That_Entity()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var foo = 5;
                var entityToAdd = new Entity { Foo = foo };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var results = unitOfWork1.GetContext<TestContext>().Query<Entity>();

                Assert.AreEqual(1, results.Count());
                Assert.AreSame(entityToAdd, results.First());
            }

            [Test]
            public void Attaches_Entity_Removed_Event()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var foo = 5;
                var entityToAdd = new SomeEntity { SomeCollection = new ObservableCollection<Entity> { new Entity { Foo = foo }, new Entity { Foo = foo + 1 } } };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var results = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, results.Count());
                Assert.AreEqual(2, results.First().SomeCollection.Count);
            }

            [Test]
            public void Removing_Entity_Deletes_From_Context()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();
                var unitOfWork2 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => new TestContext())).Initialize();

                var foo = 5;
                var entityToAdd = new SomeEntity { SomeCollection = new ObservableCollection<Entity> { new Entity { Foo = foo }, new Entity { Foo = foo + 1 } } };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var queryable = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, queryable.Count());
                var e = queryable.First();
                e.SomeCollection.RemoveAt(1);
                unitOfWork1.Commit();

                var results = unitOfWork2.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, results.Count());
                var someEntity = results.First();
                Assert.AreEqual(1, someEntity.SomeCollection.Count);
                Assert.AreEqual(foo, someEntity.SomeCollection[0].Foo);
            }

            [Test]
            public void Removing_Entity_Deletes_From_Context_When_Not_Committed()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var foo = 5;
                var entityToAdd = new SomeEntity { SomeCollection = new ObservableCollection<Entity> { new Entity { Foo = foo }, new Entity { Foo = foo + 1 } } };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var queryable = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, queryable.Count());
                var e = queryable.First();
                e.SomeCollection.RemoveAt(1);

                var results = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, results.Count());
                var someEntity = results.First();
                Assert.AreEqual(1, someEntity.SomeCollection.Count);
                Assert.AreEqual(foo, someEntity.SomeCollection[0].Foo);
            }

            [Test]
            public void Delete_Removes_Entity()
            {
                var unitOfWork = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();
                
                var foo = 5;
                var entityToAdd = new Entity { Foo = foo };

                var context = unitOfWork.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork.Commit();

                unitOfWork.GetContext<TestContext>().Delete(entityToAdd);
                unitOfWork.Commit();

                var results = unitOfWork.GetContext<TestContext>().Query<Entity>();

                Assert.AreEqual(0, results.Count());
            }

            [Test]
            public void Propertys_Can_Be_Injected()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var foo = 5;
                var entityToAdd = new SomeEntity { SomeCollection = new ObservableCollection<Entity> { new Entity { Foo = foo }, new Entity { Foo = foo + 1 } } };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var entityToInject = new Entity {Foo = foo};
                _context = new TestContext(t => t == typeof(Entity), t=> entityToInject);

                unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var result = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, result.Count());
                Assert.AreSame(entityToInject, result.First().Entity);
            }
            
            [Test]
            public void Propertys_Can_Be_Injected_Uses_Default_Context_Naming()
            {
                var unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var foo = 5;
                var entityToAdd = new SomeEntity { SomeCollection = new ObservableCollection<Entity> { new Entity { Foo = foo }, new Entity { Foo = foo + 1 } } };

                var context = unitOfWork1.GetContext<TestContext>();
                context.Add(entityToAdd);
                unitOfWork1.Commit();

                var entityToInject = new Entity { Foo = foo };
                _context = new TestContext(t => t == typeof(Entity), t => entityToInject);

                unitOfWork1 = UnitOfWork.Configure(c => c.WithContext<TestContext>(() => _context)).Initialize();

                var result = unitOfWork1.GetContext<TestContext>().Query<SomeEntity>();

                Assert.AreEqual(1, result.Count());
                Assert.AreSame(entityToInject, result.First().Entity);
                Assert.AreEqual(_context.Database.Connection.Database, "TestDb"); 
            }
        }
    }
}